//
// Created by 抑~风 on 2022/11/20.
//

#include"regexp.h"
#include<iostream>
namespace CWJ{
    using namespace std;

    RegexParser::ASTNodePtr RegexParser::EPSILON_ASTNode = make_shared<RegexParser::ASTNode>(NodeType::CHARSET,CharSet(0,0));

    RegexParser::ASTNodePtr RegexParser::parse(const RegexpLever::TokenList& tokens){
        auto& root = this->root;
        int ind = 0;
        root = this->dfs_parse_or(ind,tokens);
        return this->root;
    }

    RegexParser::ASTNodePtr RegexParser::dfs_parse_or(int& ind,const RegexpLever::TokenList& tokens){
        auto root = make_shared<ASTNode>(NodeType::OR,CharSet(0,0));
        auto len = tokens.size();
        while(ind<len){
            auto res = this->dfs_parse_and(ind,tokens);
            if(res != nullptr){
                root->addChild(dynamic_pointer_cast<ASTBaseNode>(res));
                if(ind < len && tokens[ind].getType() == RegexpLever::TokenType::OR){
                    ind ++ ;
                }
            }else break;
        }


        if(root->getChilds().empty())   return nullptr;
        else if(root->getChilds().size() == 1)  return baseNodePtr2NodePtr(root->getChilds().back());

        return root;
    };

    RegexParser::ASTNodePtr RegexParser::dfs_parse_and(int& ind,const RegexpLever::TokenList& tokens){
        auto root = std::make_shared<ASTNode>(NodeType::AND,CharSet(0,0));

        while(ind<tokens.size()){
            auto res = this->dfs_parse_str(ind,tokens);
            if(res != nullptr){
                root->addChild(res);
                if(ind < tokens.size()){

                    if(tokens[ind].getType() == TokenType::QUESMARK)   {
                        res->setMinTime(0),res->setMaxTime(1),ind++;
                    }
                    else if(tokens[ind].getType() == TokenType::ASTERISK)   {
                        res->setMinTime(0),res->setMaxTime(-1),ind++;
                    }
                    else if(tokens[ind].getType() == TokenType::PLUS)  {
                        res->setMinTime(1),res->setMaxTime(-1),ind++;
                    } else{
                        continue;
                    }

                    if(tokens[ind].getType() == TokenType::QUESMARK){
                        res->setIsGreedy(false);
                        ind ++;
                    }

                }
            }else break;
        }

        if(root->getChilds().empty())   return nullptr;
        else if(root->getChilds().size() == 1)  return baseNodePtr2NodePtr(root->getChilds().back());

        return root;
    }
    RegexParser::ASTNodePtr RegexParser::dfs_parse_str(int& ind,const RegexpLever::TokenList& tokens){

        auto len = tokens.size();
        ASTNodePtr res = nullptr;
        if(ind >= len ) return res;

        auto tn = tokens[ind];
        if(tn.getType() == TokenType::CHAR){
            if(ind + 1 < len && tokens[ind+1].getType() == TokenType::BETWEEN){
                auto next = tokens[ind+2];
               if(ind + 2 < len && next.getType() == TokenType::CHAR){
                   res = std::make_shared<ASTNode>(NodeType::CHARSET,CharSet(tn.getConText(),next.getConText()));
                   ind += 3;
               }else{
                   throw runtime_error("must char TokenType::BETWEEN char");
               }
            }else{
                res = std::make_shared<ASTNode>(NodeType::CHARSET,CharSet(tn.getConText(),tn.getConText()));
                ind += 1;
            }
        }else if(tn.getType() == TokenType::OPPOSITE){
            ind++;
            res = dfs_parse_str(ind,tokens);

            res->getContext().setIsOpposite(true);
        }else if(tn.getType() == TokenType::DOT){

            res = std::make_shared<ASTNode>(NodeType::CHARSET,CharSet(0,CharSet::INF));

            ind ++;
        }
        else if(tn.getType() == TokenType::LEFT_MIDDLE){
            ind ++;
            res = dfs_parse_middle(ind,tokens);
            if(ind >= len || tokens[ind].getType() != TokenType::RIGHT_MIDDLE){
                throw runtime_error("must [ ]");
            }
            ind ++ ;
        }else if(tn.getType() == TokenType::LEFT_SMALL){

            ind ++ ;
            res = dfs_parse_or(ind,tokens);
            if(ind >= len || tokens[ind].getType() != TokenType::RIGHT_SMALL){
                throw runtime_error("must ( ) ");
            }
            ind ++ ;

        }else if(tn.getType() == TokenType::TRANSFORM){
            ind ++;
            if(ind >= len){
                throw runtime_error("must \\char");
            }
            res = std::make_shared<ASTNode>(NodeType::CHARSET,CharSet(tokens[ind].getConText(),tokens[ind].getConText()));
            ind++;
        };
        return res;
    }

    RegexParser::ASTNodePtr RegexParser::dfs_parse_middle(int& ind,const RegexpLever::TokenList& tokens){
        auto root = std::make_shared<ASTNode>(NodeType::OR,CharSet(0,0));

        bool isOppo = false;
        if(ind < tokens.size() && tokens[ind].getType() == TokenType::OPPOSITE){
            ind ++;
            isOppo = true;
        }


        while(ind < tokens.size()){
            auto res = this->dfs_parse_str(ind,tokens);
            if(res != nullptr){
                root->addChild(res);
            }else break;
        }


        if(root->getChilds().empty())   return nullptr;
        else if(root->getChilds().size() == 1)  root = baseNodePtr2NodePtr(root->getChilds().back());

        auto charSet = root->getContext();
        charSet.setIsOpposite(isOppo);
        root->setContext(charSet);

        return root;
    }

    RegexParser::ASTBaseNodePtr RegexParser::nodePtr2BaseNodePtr(RegexParser::ASTNodePtr nodeptr) {

        return dynamic_pointer_cast<ASTBaseNode>(nodeptr);
    }

    RegexParser::ASTNodePtr RegexParser::baseNodePtr2NodePtr(RegexParser::ASTBaseNodePtr basePtr){
        return dynamic_pointer_cast<ASTNode>(basePtr);
    }

    RegexParser::ASTNode::ASTNode(const RegexParser::NodeType& nodeType,const CharSet &charSet) : ASTBaseNode(nodeType,charSet) {}

    int RegexParser::ASTNode::getMaxTime() const {
        return maxTime;
    }

    void RegexParser::ASTNode::setMaxTime(size_t maxTime) {
        ASTNode::maxTime = maxTime;
    }

    int RegexParser::ASTNode::getMinTime() const {
        return minTime;
    }

    void RegexParser::ASTNode::setMinTime(size_t minTime) {
        ASTNode::minTime = minTime;
    }

    bool RegexParser::ASTNode::isGreedy1() const {
        return isGreedy;
    }

    void RegexParser::ASTNode::setIsGreedy(bool isGreedy) {
        ASTNode::isGreedy = isGreedy;
    }


}
